#pragma once

#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include "Log.hpp"
#include <stdlib.h>
#include <cstring>
#include <strings.h>
#include <arpa/inet.h>
// #include <netinet/in.h>
#include "InetAddr.hpp"
#include <functional>
enum
{
	SOCKET_ERROR = 1,
	BIND_ERROR,
	USAGE_ERROR
};

const static int defaultfd = -1;

// std::string Translate(const std::string &word,bool &ok)
using fun_t = std::function<std::string(const std::string &, bool &)>;

// 要找到服务器，通过ip地址和端口号
class Udpserver
{
public:
	Udpserver(uint16_t port, fun_t func) : _port(port), _isrunning(false), _sockfd(defaultfd), _func(func)
	{
	}

	// 初始化主要任务是创建和绑定
	void InitServer()
	{
		// 1.创建udp server套接字,使用socket接口
		// socket，放回的是一个文件描述符,这里要实现的是跨网络通信，
		// 第一个参数选择AF_INET,因为udp是面向数据报，所以第二个参数选择SOCK_DGRAM，第三个参数默认0就行，因为前两个就可以确定了
		_sockfd = socket(AF_INET, SOCK_DGRAM, 0);
		if (_sockfd < 0) // 失败处理
		{
			LOG(FATAL, "socket error,%s,%d\n", strerror(errno), errno);
			exit(SOCKET_ERROR);
		}
		LOG(INFO, "socket create success, sockfd: %d\n", _sockfd);

		// 2.绑定ip和端口号
		// 2.1填充socket_in结构
		// 用到头文件#include <arpa/inet.h>
		struct sockaddr_in local; // struct sockaddr_in 系统提供的数据类型。local是变量，用户栈上开辟空间。int a = 100; a = 20;
		bzero(&local, sizeof(local));
		local.sin_family = AF_INET;
		local.sin_port = htons(_port); // port要经过网络传输给对面，先到网络，_port：主机序列-> 主机序列，转成网络序列
		// a. 字符串风格的点分十进制的IP地址转成 4 字节IP
		// 这里有一种方式可以利用一个结构体对象，里面分别放有4个8位的整形参数，通过强转方式来获取点分的ip地址
		// b. 主机序列，转成网络序列
		// 利用in_addr_t inet_addr(const char *cp);可以同时完成a&b
		// local.sin_addr.s_addr = inet_addr(_ip.c_str());

		// 2.2 bind sockfd和网络信息(IP(?)+port)
		int n = bind(_sockfd, (struct sockaddr *)&local, sizeof(local));
		if (n < 0)
		{
			LOG(FATAL, "bind error, %s, %d\n", strerror(errno), errno);
			exit(BIND_ERROR);
		}
		LOG(INFO, "socket bind success\n");
	}

	void Start()
	{
		// 一直运行，直到管理者不想运行了， 服务器都是死循环
		// UDP是面向数据报的协议
		_isrunning = true;
		while (true)
		{
			char buffer[1024];
			struct sockaddr_in peer;
			socklen_t len = sizeof(peer);

			// 1.要先让server接收到数据
			// ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);
			ssize_t n = recvfrom(_sockfd, &buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &len);
			if (n > 0)
			{
				buffer[n] = 0;
				InetAddr addr(peer);
				LOG(DEBUG, "get message from [%s:%d]: %s\n", addr.ip().c_str(), addr.port(), buffer);

				bool ok;
				std::string respone = _func(buffer, ok);

				// 2.将server接收到的数据，发回给对方
				sendto(_sockfd, respone.c_str(), respone.size(), 0, (struct sockaddr *)&peer, len);
			}
		}

		_isrunning = true;
	}

	~Udpserver()
	{
	}

private:
	int _sockfd;
	// std::string _ip; // ip	ip地址设置为0，会绑定任意ip，因为一台服务器可以被不同的ip地址来访问进程
	uint16_t _port;	 // 端口号，2字节16位
	bool _isrunning; // 启动标志
	fun_t _func;
};